import threading,network,player,random,gameroot,pygame,sql,random,time,loaders

SOLO_GAME = 1
NETWORK_GAME = 2
ONLINE_GAME = 3 

'''
 AI Class
 Class to transmit commands to non-localplayer entities
'''
class AI:
    def __init__(self):
        self.newCommands = []
    
    #Allows enemy instance to get all new commands, clears them once they've been read
    def getCommands(self):
        ret = self.newCommands
        self.newCommands=[]
        return ret
    
    #Adds a command to the list of new commands
    def addCommand(self,opcode):
        self.newCommands.append(opcode)

'''
 AIFactory Class
 Class which generates non-local player enemies
 based on what type of game is currently being
 played
'''
class AIFactory:
    def __init__(self,gametype,root,p):
        #A few necessary variables
        self.root=root
        self.newEnemies = []
        self.newBullets = []
        self.allBullets = {}
        self.recycle = []
        self.recycleBullets = []
        self.ais = {}
        self.player = p
        self.pid = random.randint(0,1000)
        self.player.pid = self.pid
        self.newOutMsgs = []
        self.newInMsgs = []
        self.leaderboard = []
        
        #Handle creation of a solo game ai system
        if(gametype==SOLO_GAME):
            self.allBullets = []        
            #List of currently active AI entities
            self.ais = []
            
            #Create 10 randomly controlled enemies
            for i in range(20):
                tempAI = AI()
                tempEnemy = player.Enemy(self.root,gameroot.respath+"ships/demoenemy.png",tempAI,0,(random.randint(0,5000),random.randint(0,5000)));
                self.newEnemies.append(tempEnemy)
                self.ais.append(tempAI)
            
            #Build and start AI controller
            self.controller = SoloPlayAIController(self)
            self.controller.start()
           
        #Handle creation of a networked game ai system 
        elif(gametype==NETWORK_GAME):
            
            #Dictionary of currently active AI entities {player_id:ai_instance}
            self.ais = {}
            
            self.con = network.NetworkGameHandler(self.root.prefs["network_host"],self.root.prefs["network_user"],self.root.prefs["network_password"],self.player)       
            #Build an Enemy instance for all of the currently online players 
            players = self.con.getPlayers()
            for p in players:
                tempAI = AI()                                                                           #create AI
                tempEnemy = player.Enemy(self.root,gameroot.respath+"ships/demoenemy.png",tempAI,p['pid'])             #Create enemy ship
                self.ais[p['pid']] = tempAI                                                                 #Store the ai object
                self.newEnemies.append(tempEnemy)
                #Give these new players their current set of commands
                if p['MOVE_FORWARD']:
                    self.ais[p['pid']].addCommand(network.MOVE_FORWARD)                                                      #Notify the game of the new enemy
                if p['S_MOVE_FORWARD']:
                    self.ais[p['pid']].addCommand(network.S_MOVE_FORWARD)  
                if p['MOVE_BACKWARD']:
                    self.ais[p['pid']].addCommand(network.MOVE_BACKWARD)
                if p['S_MOVE_BACKWARD']:
                    self.ais[p['pid']].addCommand(network.S_MOVE_BACKWARD)
                if p['ROTATE_LEFT']:
                    self.ais[p['pid']].addCommand(network.ROTATE_LEFT)
                if p['S_ROTATE_LEFT']:
                    self.ais[p['pid']].addCommand(network.S_ROTATE_LEFT)
                if p['ROTATE_RIGHT']:
                    self.ais[p['pid']].addCommand(network.ROTATE_RIGHT)
                if p['S_ROTATE_RIGHT']:
                    self.ais[p['pid']].addCommand(network.S_ROTATE_RIGHT)
            players = self.con.getPlayerData()
            removals = players[1]
            players = players[0]
            
            #give each player their starting location, speed, health, and shield
            for p in players:
                self.ais[p['pid']].addCommand((p['x'],p['y'],p['rot'],p['vx'],p['vy'],p['lastupdate'],p['health'],p['shield'],p['username']))
           
            #remove people who should not be updated anymore
            for p in removals:
                self.recycle.append(p)
                del self.ais[p]    
            
            #Start the network thread 
            self.cont = NetworkPlayAIController(self)
            self.cont.start()        
        #Handle creation of an online game ai system                
        elif(gametype==ONLINE_GAME):
            self.ais = {}
            self.net_ret = NetworkRetriever(self)
    
    '''
     getNew()
     Function to get all new players from the database
     @return: list of all new enemies
    ''' 
    def getNew(self):
        ret = self.newEnemies
        self.newEnemies = []
        return ret
    '''
     getNewBullets()
     Function to get all of the new bullets from the database
     @return: list of all new bullets
    '''
    def getNewBullets(self):
        ret = self.newBullets
        self.newBullets = []
        return ret
    
    '''
     getRemove()
     Function to get all of the players who need to be recycled
     @return: list of all players who need to be recycled
    '''
    def getRemove(self):
        ret = self.recycle
        self.recycle = []
        return ret
    '''
     getRemoveBullets()
     Function to get all bullets who need to be recycled
     @return: list of all bullets who need to be recycled
    '''
    def getRemoveBullets(self):
        ret = self.recycleBullets
        self.recycleBullets = []
        return ret
    '''
     appendChatMessage(String)
     Add a message to the chat database
     @param message: message to append to the database
    '''
    def appendChatMessage(self,message):
        self.newOutMsgs.append(message)
    
    '''
     getOutMessages()
     Accessor method for all message that must be sent out to the database
     @return: list of new chat messages
    '''
    def getOutMessages(self):
        temp = self.newOutMsgs
        self.newOutMsgs = []
        return temp
    '''
     getChatMessages()
     Accessor method for all messages set since the last time it was checked
     @return: list of all new chat messages from the database
    '''
    def getChatMessages(self):
        temp = self.newInMsgs
        self.newInMsgs = []
        return temp
    '''
     getLeaderboard()
     Accessor method for leaderboard information
     @return: list of dictionaries containing leaderboard information ['player']['kills']['deaths']
    '''
    def getLeaderboard(self):
        if self.leaderboard !=None:
            temp = self.leaderboard
            self.leaderboard = []
            return temp
        else:
            return []
        

'''
 NetworkRetriever Class
 Connects to the specified host and routes packets to the
 ai entities associated with the packet
 NOTE : Only for use with Online Game
'''
class NetworkRetriever(threading.Thread):
    def __init__(self,factory):
        self.factory = factory
        self.net = network.NetworkHandler()
    
    def run(self):
        while not self.root.done:
            packet = self.net.read()
            if packet.getOpcode() == network.DATA:
                pass
            elif packet.getPid() in self.factory.ais:
                self.factory.ais[packet.getPid()].addCommand(packet.getOpcode())

'''
 SoloPlayAIController Class
 handles creation of solo-play enemies, as well as runs all
 of their ai routines
'''
class SoloPlayAIController(threading.Thread):
    def __init__(self,factory):
        threading.Thread.__init__(self)
        self.factory = factory
        self.options = (network.MOVE_FORWARD,network.MOVE_BACKWARD,network.ROTATE_LEFT,network.ROTATE_RIGHT,network.S_MOVE_BACKWARD,network.S_MOVE_FORWARD,network.S_ROTATE_LEFT,network.S_ROTATE_RIGHT)
        self.clock = pygame.time.Clock()
        
    '''
     run()
     Start the thread
    '''    
    def run(self):
        while not self.factory.root.pane.done:
            for i in self.factory.ais:
                t = self.options[random.randint(0,7)]
                i.addCommand(network.SHOOT_BULLET)
                self.clock.tick(2000)
        
    
            #If weve shot, add a bullet
            conts = self.factory.player.getControls()
            if network.SHOOT_BULLET in conts:
                bid = random.randint(0,100000)
                tempBullet = player.Bullet(self.factory.root,gameroot.respath+"laser.png",bid,self.factory.pid,(self.factory.player.xpos,self.factory.player.ypos),self.factory.player.rotation,time.time())
                self.factory.newBullets.append(tempBullet)
                self.factory.allBullets.append(tempBullet.bid)

'''
 NetworkPlayAIController Class
 Connects directly to the host MySQL database and controls
 player AI entities accordingly, also handles routing
 of client control actions to the database, and stores
 client player data to the database
'''               
class NetworkPlayAIController(threading.Thread):
    def __init__(self,factory):
        threading.Thread.__init__(self)
        self.factory = factory
        self.clock = pygame.time.Clock()
    
    '''
     run()
     Start our network thread
    '''   
    def run(self):
        count = 0
        while not self.factory.root.pane.done:
            
            #Get all of the currently online players and their current control states
            players = self.factory.con.getPlayers()
            
            #Iterate through currently online players and create/remove enemy instances based on pids, then route their control states to their ai entities
            for p in players:
                #If factory doesnt have an instance for the current pid, create one
                if p['pid'] not in self.factory.ais:
                    print "New Player has Arrived - " + str(p['pid'])
                    tempAI = AI()
                    tempEnemy = player.Enemy(self.factory.root,gameroot.respath+"ships/demoenemy.png",tempAI,p['pid'])
                    self.factory.ais[p['pid']] = tempAI
                    self.factory.newEnemies.append(tempEnemy)
                #Route current control state to the ai entity of the player belonging to current pid
                if p['MOVE_FORWARD'] == 't':
                    self.factory.ais[p['pid']].addCommand(network.MOVE_FORWARD)                                                      #Notify the game of the new enemy
                if p['S_MOVE_FORWARD'] =='t':
                    self.factory.ais[p['pid']].addCommand(network.S_MOVE_FORWARD)  
                if p['MOVE_BACKWARD']=='t':
                    self.factory.ais[p['pid']].addCommand(network.MOVE_BACKWARD)
                if p['S_MOVE_BACKWARD']=='t':
                    self.factory.ais[p['pid']].addCommand(network.S_MOVE_BACKWARD)
                if p['ROTATE_LEFT']=='t':
                    self.factory.ais[p['pid']].addCommand(network.ROTATE_LEFT)
                if p['S_ROTATE_LEFT']=='t':
                    self.factory.ais[p['pid']].addCommand(network.S_ROTATE_LEFT)
                if p['ROTATE_RIGHT']=='t':
                    self.factory.ais[p['pid']].addCommand(network.ROTATE_RIGHT)
                if p['S_ROTATE_RIGHT']=='t':
                    self.factory.ais[p['pid']].addCommand(network.S_ROTATE_RIGHT)
                     
            #Every tenth of a second, update all of the player's actual data    
            if count >=100:
                players = self.factory.con.getPlayerData()
                removals = players[1]
                players = players[0]
                for p in players:
                    self.factory.ais[p['pid']].addCommand((p['x'],p['y'],p['rot'],p['vx'],p['vy'],p['lastupdate'],p['health'],p['shield'],p['username']))
                #and remove everyone who has disconnected
                for p in removals:
                    self.factory.recycle.append(p)
                    del self.factory.ais[p]
                count = 0
            #Save our position        
            self.factory.con.savePosition((self.factory.player.xpos,self.factory.player.ypos,self.factory.player.vx,self.factory.player.vy,self.factory.player.rotation))
            
            #Save our controls
            conts = self.factory.player.getControls()
            self.factory.con.setControls(conts)
            
            #Get all of the bullets from the database and check them against time and our list of bullets
            bullets = self.factory.con.getBullets()
            for b in bullets:
                
                #recycle if they were shot more than 10 seconds ago
                if b['timeshot']>10: 
                    self.factory.con.deleteBullet(b['bid'])
                    self.factory.recycleBullets.append(b['bid'])
                else:
                    #If factory doesn't have an instance for the current bid, create one
                    if b['bid'] not in self.factory.allBullets:
                        tempBullet = player.Bullet(self.factory.root,gameroot.respath+"laser.png",b['bid'],b['pid'],(b['x'],b['y']),b['rot'],time.time()-b['timeshot'])                                                             #Store the ai object
                        self.factory.newBullets.append(tempBullet)    
                        self.factory.allBullets[tempBullet.bid]=tempBullet
                        self.factory.root.audiosys.play_sfx(loaders.load_sound(gameroot.respath+"sfx/shoot.ogg"))
                        
            #if we shot a bullet, save it to the database        
            if network.SHOOT_BULLET in conts:
                bid = random.randint(0,100000)
                self.factory.con.fireBullet(bid,self.factory.pid,self.factory.player.xpos,self.factory.player.ypos,self.factory.player.rotation)                        
            
            #Send new chat messages from us to the database
            outmsg = self.factory.getOutMessages()
            for msg in outmsg:
                self.factory.con.appendChatMessage(msg)
            
            #Get all the chat messages since last time
            self.factory.newInMsgs.extend(self.factory.con.getChatMessages())
            
            #If we died, notify the leaderboard
            if len(self.factory.player.newDeaths)>0:
                for d in self.factory.player.newDeaths:
                    self.factory.con.addKill(d)
                self.factory.player.newDeaths = []
            
            #Pull down the leaderboard information    
            if self.factory.leaderboard!=None and len(self.factory.leaderboard)==0:
                self.factory.leaderboard = self.factory.con.getLeaderboard()
                   
            count = count+1
            self.clock.tick(1000)
            
            
        
        
        
        
        